using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Collections.Generic;

namespace Nate.GUI.Canvas
{
    public partial class NavMesh
    {
        public class Data
        {
            public class Exception
            {
                public class FailedToGenerateID : System.Exception { public FailedToGenerateID(string s) : base(s) { } }
            }

            public Data()
            {
                _selected = null;
                _vertices = new Hashtable();
                _edges = new Hashtable();
                _polys = new Hashtable();
                _rand = new Random( 42 /* Random is bad. I'm more interested in constant distribution. So I'm using a fixed seed. */ );
            }

            public bool Find(PointF location, out Vertex vertex)
            {
                vertex = null;
                foreach (Vertex v in Vertices)
                {
                    if (v.Rect.Contains(location))
                    {
                        vertex = v;
                        break;
                    }
                }

                return vertex != null;
            }

            private bool _Intersects(Edge e, PointF point, float epsilon)
            {
                PointF a = Get(e.A).Point;
                PointF b = Get(e.B).Point;

                NUtils.Math.Sphere  sphere  = new NUtils.Math.Sphere((double)point.X, (double)point.Y, 0.0, (double)epsilon);
                NUtils.Math.Segment segment = new NUtils.Math.Segment(a.X, a.Y, 0.0, b.X, b.Y, 0.0);

                return NUtils.Math.DoesIntersect(segment, sphere);
            }

            public bool Find(PointF location, out Edge edge)
            {
                float radius = 2.0f;
                edge = null;
                foreach (Edge e in Edges)
                {
                    if (_Intersects(e, location, radius))
                    {  
                        edge = e;
                        break;
                    }
                }

                return edge != null;
            }


            public NavMesh.Vertex CreateVertex(PointF point)
            {
                for (int tries = _vertices.Count; tries >= 0; --tries)
                {
                    NavMesh.Vertex.ID potential = new Vertex.ID(_rand.Next(1, int.MaxValue - 1));
                    if (_vertices.ContainsKey(potential))
                    {
                        continue;
                    }

                    return new Vertex( potential, point);
                }

                throw new Exception.FailedToGenerateID("Failed to generate a new Vertex ID. Try again later.");
            }

            public NavMesh.Edge CreateEdge(Vertex.ID a, Vertex.ID b)
            {
                for (int tries = _vertices.Count; tries >= 0; --tries)
                {
                    NavMesh.Edge.ID potential = new Edge.ID(_rand.Next(1, int.MaxValue - 1));
                    if (_edges.ContainsKey(potential))
                    {
                        continue;
                    }

                    return new Edge(potential, a, b);
                }

                throw new Exception.FailedToGenerateID("Failed to generate a new Edge ID. Try again later.");
            }

            public NavMesh.Poly CreatePoly()
            {
                for (int tries = _vertices.Count; tries >= 0; --tries)
                {
                    Poly.ID potential = new Poly.ID(_rand.Next(1, int.MaxValue - 1));
                    if (_edges.ContainsKey(potential))
                    {
                        continue;
                    }

                    return new Poly(potential);
                }

                throw new Exception.FailedToGenerateID("Failed to generate a new Edge ID. Try again later.");
            }

            public void Add(Edge e)
            {
                _edges.Add(e.Id, e);
            }
            public void Add(Vertex v)
            {
                _vertices.Add(v.Id, v);
            }
            public void Add(Poly p)
            {
                _polys.Add(p.Id, p);
            }

            public Edge Get(Edge.ID id)
            {
                return _edges[id] as Edge;
            }
            public Vertex Get(Vertex.ID id)
            {
                return _vertices[id] as Vertex;
            }
            public Poly Get(Poly.ID id)
            {
                return _polys[id] as Poly;
            }


            public ICollection Vertices
            {
                get
                {
                    return _vertices.Values;
                }
            }
            public ICollection Edges
            {
                get
                {
                    return _edges.Values;
                }
            }
            public ICollection Polys
            {
                get
                {
                    return _polys.Values;
                }
            }
            public object Selected
            {
                get
                {
                    return _selected;
                }
                set
                {
                    _selected = value;
                }
            }

            Hashtable _vertices;
            Hashtable _edges;
            Hashtable _polys;
            Random _rand;
            object _selected;
        }
    }
}